#ifndef CUBEGRAMMAR_H
#define CUBEGRAMMAR_H


#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/fusion/sequence/intrinsic/at_c.hpp>
#include <boost/fusion/include/at_c.hpp>
#include <boost/optional.hpp>


  #include <boost/spirit/include/phoenix_function.hpp>

#include <iostream>
#include <string>
#include "pozicijas.h"

namespace client
{
    namespace qi = boost::spirit::qi;
    namespace phoenix = boost::phoenix;
    namespace ascii = boost::spirit::ascii;









struct gajienu_impl
{

const static int pagriezieni[][24];




template<typename Arg1,typename Arg2,typename Arg3,typename ptrpozt>
    struct result
    {
        typedef  ptrpozt  type;
    };



template<typename Arg1,typename Arg2,typename Arg3,typename ptrpozt>
   ptrpozt operator()(Arg1  arg1,Arg2 arg2, Arg3 arg3,ptrpozt ptrpoz) const
    {
int pagrez[24];
int pagstrez[24];
bool fliprez = false;

for(int i = 0;i<24;i++)
{
    pagrez[i]=ptrpoz[pagriezieni[arg3][i]];
pagstrez[i]=pagriezieni[arg3][i];
}
if(arg2)
{
 int pagsk = *arg2;
  if(pagsk >1)  for(int pagnum = 1;pagnum < pagsk;pagnum++)
 {
if(fliprez)for(int i = 0;i<24;i++)pagrez[i]=pagstrez[pagriezieni[arg3][i]];
else for(int i = 0;i<24;i++)pagstrez[i]=pagrez[pagriezieni[arg3][i]];
fliprez = !fliprez;
}
}
if(arg1)
{
if(fliprez)for (int i = 0;i!=24;i++)pagrez[pagstrez[i]] = i;
else for (int i = 0;i!=24;i++)pagrez[pagstrez[i]] = i;
fliprez = !fliprez;
}

if(fliprez)for(int i = 0;i!=24;i++)ptrpoz[i] = pagstrez[i];
else for(int i = 0;i!=24;i++)
    ptrpoz[i] = pagrez[i];

 return ptrpoz;

  }
};




struct poz_impl
{
template<typename Arg1,typename Arg2,typename Arg3,typename Arg4,typename Arg5>
    struct result
    {
        typedef  Arg4  type;
    };

template<typename Arg1,typename Arg2,typename Arg3,typename Arg4,typename Arg5>
    Arg4 operator()(Arg1  arg1,Arg2 arg2, Arg3 arg3,Arg4 arg4,Arg5 arg5) const
    {
int pagrez[24];
int pagstrez[24];
bool fliprez = false;
int * pagriezieni = arg5[arg3].pozdata;
for(int i = 0;i<24;i++)pagrez[i]=pagriezieni[i];
if(arg2)
{
 int pagsk = *arg2;
  if(pagsk >1)  for(int pagnum = 1;pagnum < pagsk;pagnum++)
 {
if(fliprez)for(int i = 0;i<24;i++)pagrez[i]=pagstrez[pagriezieni[i]];
else for(int i = 0;i<24;i++)pagstrez[i]=pagrez[pagriezieni[i]];
fliprez = !fliprez;
}
}
if(arg1)
{
if(fliprez)for (int i = 0;i!=24;i++)pagrez[pagstrez[i]] = i;
else for (int i = 0;i!=24;i++)pagrez[pagstrez[i]] = i;
fliprez = !fliprez;
}

if(fliprez)for(int i = 0;i!=24;i++)arg4[i] = pagstrez[i];
else for(int i = 0;i!=24;i++)arg4[i] = pagrez[i];




    return arg4;
    }
};


struct atviek_impl
{
template<typename Arg1,typename Arg2>
        struct result
{
typedef void type;

};

template<typename Arg1,typename Arg2>
   void operator()(Arg1  arg1,Arg2 arg2) const
    {

    std::vector<int> * newpos = new std::vector<int>;

    for(int i = 0;i!=24;i++)
    {
   newpos->push_back(arg2[i]);
   arg2[i]=i;
   }
arg1->push_back(newpos);

}

};




struct aizviek_impl
{
template<typename Arg1,typename Arg2,typename Arg3,typename Arg4>
        struct result
{
typedef Arg4 type;

};

template<typename Arg1,typename Arg2,typename Arg3,typename Arg4>
   Arg4 operator()(Arg1  arg1,Arg2 arg2, Arg3 arg3,Arg4 arg4) const
    {

   int pagrez[24];
int pagstrez[24];
bool fliprez = false;
std::vector<int> * ieprstav = arg3->back();
for(int i = 0;i<24;i++)pagrez[i]=(*ieprstav)[arg4[i]];

delete ieprstav;
arg3->pop_back();

if(arg2)
{
 int pagsk = *arg2;
  if(pagsk >1)  for(int pagnum = 1;pagnum < pagsk;pagnum++)
 {
if(fliprez)for(int i = 0;i<24;i++)pagrez[i]=pagstrez[arg4[i]];
else for(int i = 0;i<24;i++)pagstrez[i]=pagrez[arg4[i]];
fliprez = !fliprez;
}
}

if(arg1)
{
if(fliprez)for (int i = 0;i!=24;i++)pagrez[pagstrez[i]] = i;
else for (int i = 0;i!=24;i++)pagrez[pagstrez[i]] = i;
fliprez = !fliprez;
}

if(fliprez)for(int i = 0;i!=24;i++)arg4[i] = pagstrez[i];
else for(int i = 0;i!=24;i++)arg4[i] = pagrez[i];




    return arg4;

}

};





    ///////////////////////////////////////////////////////////////////////////////
    //  Our calculator grammar
    ///////////////////////////////////////////////////////////////////////////////
    template <typename Iterator>
    struct calculator : qi::grammar<Iterator, int*(),ascii::space_type>
    {
        calculator(pozicijas& mypoz) : calculator::base_type(expression)
        {
            using namespace qi::labels;
            using qi::uint_;
            using qi::on_error;
            using qi::fail;
using qi::eps;
using ascii::alpha;
using ascii::char_;
using phoenix::construct;
            using phoenix::val;
                using phoenix::ref;
                pozvect = new std::vector<std::vector<int> * >;
for(int pcount=0;pcount<mypoz.count();pcount++)pozsym.add(mypoz[pcount].nosaukums.toAscii().data(),pcount);
 gajieni.add("R"    , 0)("G"   , 1)("B"  , 2)("Y"   , 3)("C"    , 4)("M"   , 5);
for(int i = 0;i!=24;i++)sakums[i]=i;
gajiens = ( -char_('-') >> -uint_ >>  gajieni)[gajfnct(_1,_2,_3,(int *)sakums)] ;
pozicija =  (!gajieni >> -char_('-') >> -uint_ >>  pozsym)[pozfnct(_1,_2,_3,(int *)sakums,ref(mypoz))];
iekavas =    (-char_('-') >> -uint_ >> char_('(')[atviekfnct(ref(pozvect),(int *)sakums)] >> (*(gajiens | pozicija | iekavas)) >> char_(')'))[aizviekfnct(_1,_2,ref(pozvect),(int *)sakums)];
expression =  (+(gajiens | pozicija | iekavas))[_val=(int *)sakums]  ;
        }

        phoenix::function<gajienu_impl>  gajfnct;
        phoenix::function<poz_impl> pozfnct;
phoenix::function<atviek_impl> atviekfnct;
phoenix::function<aizviek_impl> aizviekfnct;

std::vector<  std::vector<int> *  > * pozvect;
int sakums[24];
 qi::symbols<char, unsigned> gajieni;
qi::symbols<char, unsigned> pozsym;
        qi::rule<Iterator, int*(), ascii::space_type> gajiens,pozicija,expression,iekavas;
    };
}




#endif // CUBEGRAMMAR_H
